#include "swagme.h"
#include "sound.h"

int Sound::nFinishedChannels[NUM_AUDIO_CHANNELS];

Sound::Sound() :
    audio_rate(44100),
    audio_format(AUDIO_S16),
    audio_channels(2),
    audio_buffers(4096),
    nextHandle(0)
{
  if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers)) {
    cout << "Unable to open audio: " << Mix_GetError() << endl;
    exit(2011);
  }
  cout << "Sound: " << Mix_AllocateChannels(-1) << " channels allocated." << endl;
  Mix_AllocateChannels(NUM_AUDIO_CHANNELS);
  cout << "Sound: " << Mix_AllocateChannels(-1) << " channels allocated." << endl;
}

void Sound::channelDone(int channel) {
    // this is not an atomic write, so there is a tiny space for a race condition here
    // only way to get around it is with a semaphore
  Sound::nFinishedChannels[channel]=1;
}

void Sound::syncFinishedChannels() {
  for (int nChannel = 0; nChannel < NUM_AUDIO_CHANNELS; nChannel++) {
    if (nFinishedChannels[nChannel] && !nLastFinishedChannels[nChannel]) {
      // this channel just finished playing and the nameMap hasn't been updated
      nameMap[sChannelNameTable[nChannel]]->channel = -1;
      sChannelNameTable[nChannel]="";
    }
    nLastFinishedChannels[nChannel] = nFinishedChannels[nChannel];
  }
}

Uint32 Sound::Play(const string &name, bool loop /* =false */)
{
  int status = 0;
  int playCount = 0; // one time
  if (loop) playCount = -1; // endless loop
  syncFinishedChannels();
  if (1 == nameMap.count(name)) {
    nameMap[name]->channel = Mix_PlayChannel(-1, nameMap[name]->pChunk, playCount);
    if (-1 == nameMap[name]->channel) {
      cerr << "Mixer Error: " << Mix_GetError() << endl;
      status = 3001;
    }
    sChannelNameTable[nameMap[name]->channel] = name;
    Mix_ChannelFinished(this->channelDone);
  }
  return status;
}

bool Sound::IsPlaying(const string &name)
{
  bool bResult = false;
  syncFinishedChannels();
  if ((1 == nameMap.count(name)) && (nameMap[name]->channel > -1)) {
    bResult = Mix_Playing(nameMap[name]->channel);
  }
  return bResult;
}

void Sound::Stop(const string &name) {
  syncFinishedChannels();
  if ((1 == nameMap.count(name)) && (nameMap[name]->channel > -1)) {
    Mix_HaltChannel(nameMap[name]->channel); // always returns 0
  } else {
    cerr << "Sound: Attempted to stop " << name << ", but it's not playing." << endl;
  }
}

Uint32 Sound::Load(const string &file, const string &name)
{
  Uint32 status=0;
  if (0 == nameMap.count(name)) {
    LoadedSound *pls = new LoadedSound();
    if (pls) {
      pls->pChunk = Mix_LoadWAV(file.c_str());
    } else {
      cerr << "Sound: new failed for " << name << endl;
      exit(2012);
    }
    if (!pls->pChunk) {
      cerr << "Sound: failed to load " << file << ", Mixer Error: " << Mix_GetError() << endl;
      status = 3000;
    }
    nameMap[name] = pls;
  } else {
    cerr << "Sound: " << name << " already loaded." << endl;
  }
  return status;
}

Uint32 Sound::Unload(const string &name)
{
  if (1 == nameMap.count(name)) {
    // TODO: stop sound if it's playing
    if (nameMap[name]->pChunk) Mix_FreeChunk(nameMap[name]->pChunk);
    nameMap[name]->pChunk=NULL;
    if (nameMap[name]) {
      delete nameMap[name];
    } else {
      cerr << "Sound: " << name << " was already freed." << endl;
    }
    nameMap.erase(name);
  }
  return 0;
}

Sound::~Sound()
{
  // TODO: stop all playing sounds, unload them and free resources
  // stop all playing sounds
  // unload them
  for (nameMapIter_t iter = nameMap.begin(); iter != nameMap.end(); ++iter) {
    cout << "Unloading sound " << iter->first << endl;
    Stop(iter->first);
    Unload(iter->first);
  }
  int frequency;
  Uint16 format;
  int channels;
  int numTimes = Mix_QuerySpec(&frequency, &format, &channels);
  cout << "Sound: Mix_OpenAudio was called " << numTimes << " times." << endl;
  // free resources
  Mix_CloseAudio();
  cerr << Mix_GetError();
}


